package main

import (
	"net"
	"fmt"
	"time"
	"io/ioutil"
	"os"
	"bufio"
	"strings"
	"io"
	"crypto/md5"
	"encoding/hex"
)

const (
	localListenAddr string = "127.0.0.1:8888"

	tcpRecvBufSize int = 1024

	matchAns string = "err  0"
	disMatchAns string = "err  1"

	configFile string = "./config"
	listFile string = "./list"
)

var (
	BMatchAns []byte = []byte(matchAns)
	BDisMatchAns []byte = []byte(disMatchAns)

	needToCheck bool = false

	list = make(map[string]string)
)

func main() {
	readParseConfig()
	readMatchListConfig()
	fmt.Println("list =", list)

	listener, err := net.Listen("tcp", localListenAddr)
	if err != nil {
		fmt.Println("listen fail !", localListenAddr)
		return
	}

	fmt.Println("start listening at", listener.Addr().String())
	for {
		conn, err := listener.Accept()
		if err != nil {
			fmt.Println("accept fail !")
			time.Sleep(2 * time.Second)
			continue
		}

		go tcpRecvThread(conn)
	}
}

func tcpRecvThread(conn net.Conn) {
	defer conn.Close()

	dataBuf := make([]byte, tcpRecvBufSize)
	for {
		packageSize, err := conn.Read(dataBuf[0:])
		if err != nil {
			fmt.Println("disconnect from", conn.RemoteAddr().String())
			return
		}

		if packageSize <= 0 || packageSize > tcpRecvBufSize {
			fmt.Println("packageSize error !", packageSize)
			return
		}

		fmt.Println("recv tcp data from", conn.RemoteAddr().String(), dataBuf[0:packageSize])
		parseProtoData(conn, dataBuf[0:packageSize])
	}
}

func parseProtoData(conn net.Conn, recvData []byte) {
	fmt.Println("recv:", string(recvData))

	result := true
	if needToCheck == true && checkAuth(string(recvData[0:len(recvData)-1])) == false {
		result = false
	}
	fmt.Println("parseProtoData: result =", result)

	if result {
		conn.Write(BMatchAns)
	} else {
		conn.Write(BDisMatchAns)
	}
}

func readParseConfig() {
	data, err := ioutil.ReadFile(configFile)
	if err != nil {
		fmt.Println("can not find config file", configFile, "! prepare not to check auth !")
		needToCheck = false
		return
	}

	fmt.Println("readParseConfig:data =", string(data))
	if data[0] == '1' {
		needToCheck = true
	} else {
		needToCheck = false
	}

	fmt.Println("needToCheck = ", needToCheck)
}

func readMatchListConfig() {
	f, err := os.Open(listFile)
	if err != nil {
		fmt.Println("can not find listFile", listFile, "! prepare to deny all check auth !")
		return
	}

	buf := bufio.NewReader(f)
	for {
		line, err := buf.ReadString('\n')
		line = strings.TrimSpace(line)
		fmt.Println("line =", line)
		handleOneListFileLine(line)
		if err != nil {
			if err == io.EOF {
				return
			}
			return
		}
	}
	return
}

func handleOneListFileLine(line string) {
	r := strings.Split(line, "&")
	if len(r) != 2 {
		fmt.Println("len(r) != 2 ! line =", line)
		return
	}

	value, ok := list[r[0]]
	if ok {
		fmt.Println(r[0], "is in list ! old =", value)
		return
	}

	v := calcMD5(r[1])
	fmt.Println(r[0], r[1], v)
	list[r[0]] = v
}

func calcMD5(s string) string {
	hash := md5.New()
	hash.Write([]byte(s))
	return hex.EncodeToString(hash.Sum(nil))
}

func checkAuth(s string) bool {
	/*source := strings.Split(s, "?")
	l := len(source)
	if l != 2 && l != 1{
		fmt.Println("checkAuth: ? error !", source, l)
		return false
	}

	var subs string
	if l == 1 {
		subs = source[0]
	} else {
		subs = source[1]
	}
	
	r := strings.Split(subs, "&")*/
	r := strings.Split(s, "&")
	if len(r) != 2 {
		fmt.Println("len(r) != 2 ! s =", s)
		return false
	}

	value, ok := list[r[0]]
	if !ok {
		fmt.Println(r[0], "user name is not register !", s, r[0], r[1])
		return false
	}

	if strings.Compare(r[1], value) != 0 {
		fmt.Println("not match !", s, r[0], r[1], value, []byte(value), []byte(r[1]))
		return false
	}

	fmt.Println("match !", s, r[0], r[1])
	return true
}
